package org.jetlinks.pro.things.impl.rpc;

import lombok.AllArgsConstructor;
import lombok.Generated;
import lombok.Getter;
import lombok.Setter;
import org.hswebframework.web.bean.FastBeanCopier;
import org.jetlinks.core.things.ThingRpcSupport;
import org.jetlinks.pro.things.ProviderProperties;
import org.jetlinks.pro.things.ThingsRpcSupportProvider;
import org.jetlinks.pro.things.ThingsManagerContext;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Map;

@AllArgsConstructor
@Component
public class CompositeThingsRpcSupportProvider implements ThingsRpcSupportProvider {

    private final ThingsManagerContext context;

    @Override
    @Generated
    public String getId() {
        return "composite";
    }

    @Override
    @Generated
    public String getName() {
        return "组合RPC";
    }

    @Override
    public Mono<ThingRpcSupport> createSupport(Map<String, Object> configuration) {
        CompositeThingRpcSupportProperties properties = FastBeanCopier.copy(configuration, new CompositeThingRpcSupportProperties());
        properties.validate();

        CompositeThingsRpcSupport support = new CompositeThingsRpcSupport();
        support.setParallel(properties.parallel);
        support.setInterruptOnError(properties.interruptOnError);

        return Flux
            .fromIterable(properties.getActions())
            .index()
            .flatMap(tp2 -> context
                .createRpcSupport(tp2.getT2().getProvider(), tp2.getT2().getConfiguration())
                .doOnNext(rpcSupport -> support.addSupport(tp2.getT1().intValue(), rpcSupport)))
            .then(
                Mono
                    .justOrEmpty(properties.getConverter())
                    .flatMap(converter -> context.createConverter(converter.getProvider(), converter.getConfiguration()))
                    .doOnNext(support::setResponseConverter)
            )
            .thenReturn(support);
    }

    @Getter
    @Setter
    public static class CompositeThingRpcSupportProperties {
        private boolean parallel = true;
        private boolean interruptOnError = true;
        private List<ProviderProperties> actions;
        private ProviderProperties converter;

        public void validate() {
            Assert.notEmpty(actions, "actions can not be empty");
            actions.forEach(ProviderProperties::validate);
        }
    }
}
